package com.abel.bigwater.dataserver.model

import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.Key
import java.security.Security
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

object PacketUtil {
    init {
        Security.addProvider(BouncyCastleProvider())
    }

    /**
     * CRC16相关计算
     * encode: utf-8
     *
     * @author trb
     * @date 2014-12-25
     */
    internal var crc16_tab_h = byteArrayOf(0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x01.toByte(), 0xC0.toByte(), 0x80.toByte(), 0x41.toByte(), 0x00.toByte(), 0xC1.toByte(), 0x81.toByte(), 0x40.toByte())

    internal var crc16_tab_l = byteArrayOf(0x00.toByte(), 0xC0.toByte(), 0xC1.toByte(), 0x01.toByte(), 0xC3.toByte(), 0x03.toByte(), 0x02.toByte(), 0xC2.toByte(), 0xC6.toByte(), 0x06.toByte(), 0x07.toByte(), 0xC7.toByte(), 0x05.toByte(), 0xC5.toByte(), 0xC4.toByte(), 0x04.toByte(), 0xCC.toByte(), 0x0C.toByte(), 0x0D.toByte(), 0xCD.toByte(), 0x0F.toByte(), 0xCF.toByte(), 0xCE.toByte(), 0x0E.toByte(), 0x0A.toByte(), 0xCA.toByte(), 0xCB.toByte(), 0x0B.toByte(), 0xC9.toByte(), 0x09.toByte(), 0x08.toByte(), 0xC8.toByte(), 0xD8.toByte(), 0x18.toByte(), 0x19.toByte(), 0xD9.toByte(), 0x1B.toByte(), 0xDB.toByte(), 0xDA.toByte(), 0x1A.toByte(), 0x1E.toByte(), 0xDE.toByte(), 0xDF.toByte(), 0x1F.toByte(), 0xDD.toByte(), 0x1D.toByte(), 0x1C.toByte(), 0xDC.toByte(), 0x14.toByte(), 0xD4.toByte(), 0xD5.toByte(), 0x15.toByte(), 0xD7.toByte(), 0x17.toByte(), 0x16.toByte(), 0xD6.toByte(), 0xD2.toByte(), 0x12.toByte(), 0x13.toByte(), 0xD3.toByte(), 0x11.toByte(), 0xD1.toByte(), 0xD0.toByte(), 0x10.toByte(), 0xF0.toByte(), 0x30.toByte(), 0x31.toByte(), 0xF1.toByte(), 0x33.toByte(), 0xF3.toByte(), 0xF2.toByte(), 0x32.toByte(), 0x36.toByte(), 0xF6.toByte(), 0xF7.toByte(), 0x37.toByte(), 0xF5.toByte(), 0x35.toByte(), 0x34.toByte(), 0xF4.toByte(), 0x3C.toByte(), 0xFC.toByte(), 0xFD.toByte(), 0x3D.toByte(), 0xFF.toByte(), 0x3F.toByte(), 0x3E.toByte(), 0xFE.toByte(), 0xFA.toByte(), 0x3A.toByte(), 0x3B.toByte(), 0xFB.toByte(), 0x39.toByte(), 0xF9.toByte(), 0xF8.toByte(), 0x38.toByte(), 0x28.toByte(), 0xE8.toByte(), 0xE9.toByte(), 0x29.toByte(), 0xEB.toByte(), 0x2B.toByte(), 0x2A.toByte(), 0xEA.toByte(), 0xEE.toByte(), 0x2E.toByte(), 0x2F.toByte(), 0xEF.toByte(), 0x2D.toByte(), 0xED.toByte(), 0xEC.toByte(), 0x2C.toByte(), 0xE4.toByte(), 0x24.toByte(), 0x25.toByte(), 0xE5.toByte(), 0x27.toByte(), 0xE7.toByte(), 0xE6.toByte(), 0x26.toByte(), 0x22.toByte(), 0xE2.toByte(), 0xE3.toByte(), 0x23.toByte(), 0xE1.toByte(), 0x21.toByte(), 0x20.toByte(), 0xE0.toByte(), 0xA0.toByte(), 0x60.toByte(), 0x61.toByte(), 0xA1.toByte(), 0x63.toByte(), 0xA3.toByte(), 0xA2.toByte(), 0x62.toByte(), 0x66.toByte(), 0xA6.toByte(), 0xA7.toByte(), 0x67.toByte(), 0xA5.toByte(), 0x65.toByte(), 0x64.toByte(), 0xA4.toByte(), 0x6C.toByte(), 0xAC.toByte(), 0xAD.toByte(), 0x6D.toByte(), 0xAF.toByte(), 0x6F.toByte(), 0x6E.toByte(), 0xAE.toByte(), 0xAA.toByte(), 0x6A.toByte(), 0x6B.toByte(), 0xAB.toByte(), 0x69.toByte(), 0xA9.toByte(), 0xA8.toByte(), 0x68.toByte(), 0x78.toByte(), 0xB8.toByte(), 0xB9.toByte(), 0x79.toByte(), 0xBB.toByte(), 0x7B.toByte(), 0x7A.toByte(), 0xBA.toByte(), 0xBE.toByte(), 0x7E.toByte(), 0x7F.toByte(), 0xBF.toByte(), 0x7D.toByte(), 0xBD.toByte(), 0xBC.toByte(), 0x7C.toByte(), 0xB4.toByte(), 0x74.toByte(), 0x75.toByte(), 0xB5.toByte(), 0x77.toByte(), 0xB7.toByte(), 0xB6.toByte(), 0x76.toByte(), 0x72.toByte(), 0xB2.toByte(), 0xB3.toByte(), 0x73.toByte(), 0xB1.toByte(), 0x71.toByte(), 0x70.toByte(), 0xB0.toByte(), 0x50.toByte(), 0x90.toByte(), 0x91.toByte(), 0x51.toByte(), 0x93.toByte(), 0x53.toByte(), 0x52.toByte(), 0x92.toByte(), 0x96.toByte(), 0x56.toByte(), 0x57.toByte(), 0x97.toByte(), 0x55.toByte(), 0x95.toByte(), 0x94.toByte(), 0x54.toByte(), 0x9C.toByte(), 0x5C.toByte(), 0x5D.toByte(), 0x9D.toByte(), 0x5F.toByte(), 0x9F.toByte(), 0x9E.toByte(), 0x5E.toByte(), 0x5A.toByte(), 0x9A.toByte(), 0x9B.toByte(), 0x5B.toByte(), 0x99.toByte(), 0x59.toByte(), 0x58.toByte(), 0x98.toByte(), 0x88.toByte(), 0x48.toByte(), 0x49.toByte(), 0x89.toByte(), 0x4B.toByte(), 0x8B.toByte(), 0x8A.toByte(), 0x4A.toByte(), 0x4E.toByte(), 0x8E.toByte(), 0x8F.toByte(), 0x4F.toByte(), 0x8D.toByte(), 0x4D.toByte(), 0x4C.toByte(), 0x8C.toByte(), 0x44.toByte(), 0x84.toByte(), 0x85.toByte(), 0x45.toByte(), 0x87.toByte(), 0x47.toByte(), 0x46.toByte(), 0x86.toByte(), 0x82.toByte(), 0x42.toByte(), 0x43.toByte(), 0x83.toByte(), 0x41.toByte(), 0x81.toByte(), 0x80.toByte(), 0x40.toByte())

    /**
     * 计算CRC16校验
     *
     * @param data
     * 需要计算的数组
     * @param offset
     * 起始位置
     * @param len
     * 长度
     * @param preval
     * 之前的校验值
     * @return CRC16校验值
     */
    @JvmOverloads
    fun calcCrc16(data: ByteArray, offset: Int = 0, len: Int = data.size, preval: Int = 0xffff): Int {
        var ucCRCHi = preval and 0xff00 shr 8
        var ucCRCLo = preval and 0x00ff
        var iIndex: Int
        for (i in 0 until len) {
            iIndex = ucCRCLo xor data[offset + i].toInt() and 0x00ff
            ucCRCLo = ucCRCHi xor crc16_tab_h[iIndex].toInt()
            ucCRCHi = crc16_tab_l[iIndex].toInt()
        }
        return ucCRCHi and 0x00ff shl 8 or (ucCRCLo and 0x00ff and 0xffff)
    }

    const val ALGO = "AES"
    const val TRANS = "AES/ECB/PKCS7Padding"
    const val INIT_PWD = "CHANGSHAWATERGRO"

    fun generateKey(keyBytes: ByteArray): Key {
        return SecretKeySpec(keyBytes, ALGO)
    }

    /**
     * AES加密支持多种填充方式，NoPadding，PKCS5Padding，ISO10126Padding，ZerosPadding，PKCS7Padding。
    其中PKCS7Padding 就是数据个数最后少几个就填充多少个数，具体的做法可以：数据的个数先取余16，然后16减去余数。
    例如{1,2,3,4,5,6,7,8,9}，总共9个数值，取余16后是9，需要补充7个7，则最后数据变为{1,2,3,4,5,6,7,8,9,7,7,7,7,7,7,7}
    需要特别注意一种情况：如果数据的个数刚好是16的倍数，那就要在补充16个字节（一定要）。
    例如{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}，填充后的数据是{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,16,16,16,16，16,16,16,16,16,16,16,16,16,16,16}

    ZerosPadding就是缺少多少位就补充多少个0，基本流程相同。
     */
    fun aesEncrypt(plain: ByteArray, key: Key, from: Int, len: Int): ByteArray {
        val cipher = Cipher.getInstance(TRANS, "BC")
        val iv = IvParameterSpec(INIT_PWD.toByteArray(Charsets.ISO_8859_1))
        cipher.init(Cipher.ENCRYPT_MODE, key)
        return cipher.doFinal(plain, from, len)
    }

    fun aesDecrypt(secret: ByteArray, key: Key, from: Int, len: Int): ByteArray {
        val cipher = Cipher.getInstance(TRANS, "BC")
        val iv = IvParameterSpec(INIT_PWD.toByteArray(Charsets.ISO_8859_1))
        cipher.init(Cipher.DECRYPT_MODE, key)
        return cipher.doFinal(secret, from, len)
    }

    /**
     * encrypting kming data.
     */
    fun kmingEncrpt(plain: ByteArray, from: Int = 0): ByteArray {
        val key = generateKey(INIT_PWD.toByteArray(Charsets.ISO_8859_1))
        return aesEncrypt(plain, key, from, plain.size - from)
    }

    /**
     * decrypting kming data.
     */
    fun kmingDecrypt(secret: ByteArray, from: Int = 0): ByteArray {
        val key = generateKey(INIT_PWD.toByteArray(Charsets.ISO_8859_1))
        return aesDecrypt(secret, key, from, secret.size - from)
    }
}